﻿using PlutoStudio.Application.Navigation;
using PlutoStudio.Authorization;
using PlutoStudio.Configuration;
using PlutoStudio.Dependency;
using PlutoStudio.Localization;
using PlutoStudio.Runtime.Session;
using PlutoStudio.Web.Models.UserConfiguration;
using PlutoStudio.Web.Security.AntiForgery;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PlutoStudio.Web.Configuration
{
    public class UserConfigurationBuilder : ITransientDependency
    {
        private readonly ILanguageManager _languageManager;
        private readonly ILocalizationManager _localizationManager;
        private readonly IPermissionManager _permissionManager;
        private readonly IUserNavigationManager _userNavigationManager;
        private readonly ISettingDefinitionManager _settingDefinitionManager;
        private readonly ISettingManager _settingManager;
        private readonly IAntiForgeryConfiguration _antiForgeryConfiguration;
        private readonly ISession _session;
        private readonly IPermissionChecker _permissionChecker;

        public UserConfigurationBuilder(
            ILanguageManager languageManager,
            ILocalizationManager localizationManager,
            IPermissionManager permissionManager,
            IUserNavigationManager userNavigationManager,
            ISettingDefinitionManager settingDefinitionManager,
            ISettingManager settingManager,
            IAntiForgeryConfiguration antiForgeryConfiguration,
            ISession session,
            IPermissionChecker permissionChecker)
        {
            _languageManager = languageManager;
            _localizationManager = localizationManager;
            _permissionManager = permissionManager;
            _userNavigationManager = userNavigationManager;
            _settingDefinitionManager = settingDefinitionManager;
            _settingManager = settingManager;
            _antiForgeryConfiguration = antiForgeryConfiguration;
            _session = session;
            _permissionChecker = permissionChecker;
        }

        public async Task<UserConfigurationDto> GetAll()
        {
            return new UserConfigurationDto
            {
                Session = GetUserSessionConfig(),
                Localization = GetUserLocalizationConfig(),
                Auth = await GetUserAuthConfig(),
                Nav = await GetUserNavConfig(),
                Setting = await GetUserSettingConfig(),
                Security = GetUserSecurityConfig()
            };
        }

        private UserSessionConfigDto GetUserSessionConfig()
        {
            return new UserSessionConfigDto
            {
                UserId = _session.UserId,
                ImpersonatorUserId = _session.ImpersonatorUserId,
            };
        }

        private UserLocalizationConfigDto GetUserLocalizationConfig()
        {
            var currentCulture = CultureInfo.CurrentUICulture;
            var languages = _languageManager.GetLanguages();

            var config = new UserLocalizationConfigDto
            {
                CurrentCulture = new UserCurrentCultureConfigDto
                {
                    Name = currentCulture.Name,
                    DisplayName = currentCulture.DisplayName
                },
                Languages = languages.ToList()
            };

            if (languages.Count > 0)
            {
                config.CurrentLanguage = _languageManager.CurrentLanguage;
            }

            var sources = _localizationManager.GetAllSources().OrderBy(s => s.Name).ToArray();
            config.Sources = sources.Select(s => new LocalizationSourceDto
            {
                Name = s.Name,
                Type = s.GetType().Name
            }).ToList();

            config.Values = new Dictionary<string, Dictionary<string, string>>();
            foreach (var source in sources)
            {
                var stringValues = source.GetAllStrings(currentCulture).OrderBy(s => s.Name).ToList();
                var stringDictionary = stringValues
                    .ToDictionary(_ => _.Name, _ => _.Value);
                config.Values.Add(source.Name, stringDictionary);
            }

            return config;
        }


        private async Task<UserAuthConfigDto> GetUserAuthConfig()
        {
            var config = new UserAuthConfigDto();

            var allPermissionNames = _permissionManager.GetAllPermissions().Select(p => p.Name).ToList();
            var grantedPermissionNames = new List<string>();

            if (_session.UserId.HasValue)
            {
                foreach (var permissionName in allPermissionNames)
                {
                    if (await _permissionChecker.IsGrantedAsync(permissionName))
                    {
                        grantedPermissionNames.Add(permissionName);
                    }
                }
            }

            config.AllPermissions = allPermissionNames.ToDictionary(permissionName => permissionName, permissionName => "true");
            config.GrantedPermissions = grantedPermissionNames.ToDictionary(permissionName => permissionName, permissionName => "true");

            return config;
        }

        private async Task<UserNavConfigDto> GetUserNavConfig()
        {
            var userMenus = await _userNavigationManager.GetMenusAsync(_session.ToUserIdentifier());
            return new UserNavConfigDto
            {
                Menus = userMenus.ToDictionary(userMenu => userMenu.Name, userMenu => userMenu)
            };
        }

        private async Task<UserSettingConfigDto> GetUserSettingConfig()
        {
            var config = new UserSettingConfigDto
            {
                Values = new Dictionary<string, string>()
            };

            var settingDefinitions = _settingDefinitionManager
                .GetAllSettingDefinitions()
                .Where(sd => sd.IsVisibleToClients);

            foreach (var settingDefinition in settingDefinitions)
            {
                var settingValue = await _settingManager.GetSettingValueAsync(settingDefinition.Name);
                config.Values.Add(settingDefinition.Name, settingValue);
            }

            return config;
        }


        private UserSecurityConfigDto GetUserSecurityConfig()
        {
            return new UserSecurityConfigDto()
            {
                AntiForgery = new UserAntiForgeryConfigDto
                {
                    TokenCookieName = _antiForgeryConfiguration.TokenCookieName,
                    TokenHeaderName = _antiForgeryConfiguration.TokenHeaderName
                }
            };
        }
    }
}
